package Server;


import java.io.*;
import java.util.*;


/**
 * <p>Title: Request.java</p>
 *
 * <p>Description: Used to store and process requests that come from the client
 * to the server. </p>
 *
 * @Author: YingLuan Tan, Yves Galang
 * @Course: CSC667
 * @Date: 2012 Spring
 * 
 */
public class Request {

    /**
     * set up vars
     */
    public Request() {
        _method = "";
        _request_uri = "";
        _http_ver = "";
        _query = "";
        _header = new HashMap<String, String>();
    }

    /**
     * Parse the incoming request depending on the type of request
     *
     * @param stream 	InputStream which grabs the incoming message from the
     * 					client socket
     */
    public void parse(InputStream stream) throws IOException 
    {
        BufferedReader _in = new BufferedReader(new InputStreamReader(stream));

        if (!_in.ready()) 
        {
            try {
                for(int i = 0; i < 100 && !_in.ready(); i++)
                	Thread.sleep(50);
            } catch (InterruptedException ex) {
            	System.err.print(ex.getMessage() + "while waiting for input stream from client.\r\n\r\n");
            }
            
            if (!_in.ready()) 
                throw new IOException("Input Stream not ready. Will Drop this request.");
        }
        
        String tmp;
        tmp = _in.readLine();
        
        parseFirstLine(tmp);
        
        tmp = _in.readLine();
        while (tmp != null) 
        {    
            if (tmp.contains(":")) 
            	parseHeaders(tmp);
            else 
            {
            	parseBody(_in);
                break;
            }
            tmp = _in.readLine();
        }
    }



	/**
     * Used to first check whether a requested file path has an alias set within
     * the configuration file and if so, replaces the alias of the file or
     * directory with the real path. This way, the server can find the right
     * file in the tree.
     *
     * HINT: Remember that any one path can have multiple aliases found within
     * the httpd.conf file. For example, the URI
     * http://www.blah.net/blah/help/hello.html could have an alias where blah
     * is equivalent to http://www.blah.net/blah_blah_blah and help could be an
     * alias for http://www.blah.net/blah_blah_blah/bleh/help. Another thing to
     * note is that your URI could also include script aliases which means you
     * may be required to run a cgi script.
     *
     * @param config HttpdConf Object which contains all the information on how
     * the server is configured.
     */
/*    public void setHttpdConf(HttpdConf config) 
    {
        String tmp = config.solveAlias(_request_uri);
        if (tmp != null) 
            _request_uri = tmp;       
    }
*/
 
    /**
     * Print function used for debugging purposes. Helpful to make sure you are
     * parsing the incoming request properly.
     */
    public void print() {
        System.out.println("The method was " + _method);
        System.out.println("The Request URL was " + _request_uri);
        System.out.println("The query string was " + _query);
        System.out.println("The HTTP version is " + _http_ver);

        System.out.println("The message body was: \n" + _body + "\r\n");
    }

    
    /**
     * private function used by request object to parse the information passed
     * through from the client to the server and save it for future use. The
     * type of request can be found on this first line of the request.
     *
     * @param first String representation of the first line of the request from
     * the client. Passed in as one long string which can easily be parsed.
     */
    private void parseFirstLine(String first) throws IOException {
        String[] buf = first.split(" ");
        if (!checkRequest(buf[0])) {
            throw new IOException("Bad Request");
        }

        _method = buf[0];

        if (buf[1].startsWith("HTTP/")) {
            _http_ver = buf[1].substring(5);
        } else {
            if (buf[1].contains("?")) {
                _request_uri = buf[1].substring(0, buf[1].indexOf("?"));
                _query = buf[1].substring(buf[1].indexOf("?") + 1);
            } else {
                _request_uri = buf[1];
            }

            _http_ver = buf[2].substring(5);
        }
    }
    
    
    /**
     * parses and stores incoming request's headers
     * @param headerLine	the current line to be processed
     */
    private void parseHeaders(String headerLine)
    {
        String[] header = headerLine.split("\\: ");
        if (header.length > 1) 
            _header.put(header[0], header[1]);	
    }
    
    /**
     * handles the request's body
     * 
     * @param input
     */
    private void parseBody(BufferedReader input) 
    {
    	try{
    		if (_method.contains("POST") || _method.contains("PUT")) 
    		{
    			String contentLength = (String) _header.get("Content-Length");
    			if (contentLength != null) 
    			{
    				int length = Integer.parseInt(contentLength.trim());
    				_body = new byte[length];
    				int count = 0;
    				while (count < length)
    					if (input.ready()) 
    					{
    						int byteRead = input.read();
    						_body[count++] = (byte) byteRead;
    					}

    			}
    		}
    	} catch (IOException e) 
    	{
    		e.printStackTrace();
    	}
    	
	}
    
    
    /**
     * private function used by the request object to determine whether an
     * incoming request is a valid request or not. Useful when throwing error
     * messages.
     *
     * @return true if request is valid, false otherwise
     */
    private boolean checkRequest(String st) {
        if (st.equals("GET") || st.equals("HEAD") || st.equals("POST") || st.equals("PUT")) 
            return true;
        else 
            return false;
    }



    public String getMethod() {
        return _method;
    }

    public String getRequest_uri(){
        return _request_uri;
    }

    public String getHttp_ver() {
        return _http_ver;
    }

    public String getQuery() {
        return _query;
    }

    public byte[] getBody() {
        return _body;
    }

    public HashMap<String, String> getHeader() {
        return _header;
    }
    
 /*   public String getLog(){
    	logger.trimToSize();
    	logger.
    	return 
    }
 */ 
    
    private String _method;
    private String _request_uri;
    private String _http_ver;
    private String _query;
    private byte[] _body;
    private HashMap<String, String> _header;
    
    
//    private StringBuffer logger = new StringBuffer(1024);

    /*
    public static void main(String[] args)
    {
    Request tester = new Request();
    try {
    tester.parse(str);
    } catch (IOException ex) {
    Logger.getLogger(Request.class.getName()).log(Level.SEVERE, null, ex);
    }
    tester.print();
    }
     */
}

